package p.ithorns.framework.logger.websocket;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.timeout.IdleStateEvent;

import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.yeauty.annotation.*;
import org.yeauty.pojo.Session;
import p.ithorns.framework.common.utils.StringUtil;
import p.ithorns.framework.logger.appender.PipedStreamAppender;
import p.ithorns.framework.logger.UniDataMsg;

import java.io.IOException;

/**
 * LogWebSocket
 *
 * @author ithorns
 * @version 1.0.0
 * @date 2023/3/10 9:58
 */
@Component
@ServerEndpoint(
        // path：必须以'/'开始
        path = "${app.websocket.log.path:/log}",
        port = "${app.websocket.log.port:6090}"
)
public class LogWebSocketEndpoint {

    private final static org.slf4j.Logger log = LoggerFactory.getLogger(LogWebSocketEndpoint.class);

    private final static PipedStreamAppender APPENDER;

    public LogWebSocketEndpoint() {
        System.out.println("=======> LogWebSocket is boot");
    }

    static {
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        Logger rootLogger = loggerContext.getLogger("ROOT");
        APPENDER = (PipedStreamAppender) rootLogger.getAppender("PIPE");
    }

    @BeforeHandshake
    public void handshake(Session session, HttpHeaders headers) {
        String token = headers.get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
        session.setSubprotocols(token);
        if (StringUtil.isBlank(token)) {
            log.info("Authentication failed! {}", session.id());
            session.close();
        }
    }


    @OnOpen
    public void onOpen(Session session, HttpHeaders headers) {
        Channel channel = session.channel();
        final ChannelId id = channel.id();
        log.info("WebSocket 打开连接：{}", id);

        final String clientId = headers.get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL) + ":" + id;
        // 注册客户端Channel
        WsSupervise.register(clientId, channel);

        // 直接加入消费者列表
        APPENDER.apply(id.asShortText(), line -> {
            // 将实时日志通过WebSocket发送给客户端
            WsSupervise.sendToSimple(clientId, line);
        });
    }


    @OnMessage
    public void onMessage(Session session, String message) throws IOException {
        log.info(message);
    }

    @OnClose
    public void onClose(Session session) {
        Channel channel = session.channel();
        WsSupervise.removeChannel(channel);

        String id = getChannelId(session);
        APPENDER.remove(id);

        log.info("==> Session closed: {}", id);
    }

    @OnError
    public void onError(Session session, Throwable t) throws IOException {
        String id = getChannelId(session);
        log.info("--> WebSocket[{}] error.", id, t);
    }

    @OnEvent
    public void onEvent(Session session, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            switch (idleStateEvent.state()) {
                case READER_IDLE:
                    log.info("长期无消息, 主动断线. {}", session.channel());
                    session.close();
                    break;
                case WRITER_IDLE:
                    log.info("write idle");
                    break;
                case ALL_IDLE:
                    log.info("all idle");
                    break;
                default:
                    break;
            }
        }
    }

    private String getChannelId(Session session) {
        return session.id().asShortText();
    }


    private UniDataMsg buildMsg(String clientId, String content) {
        UniDataMsg msg = new UniDataMsg();
        msg.setUuid(clientId);
        msg.setMsg(content);
        return msg;
    }

}